/* 
 *  include/cos/protect.h
 * 
 *  created at 2007.10.02 by ChenJie
 *	  
*	last modified at 2007.10.02
 *
 */
#ifndef PROTECT_H
#define PROTECT_H

#include <asm/i386.h>
#include <cos/sched.h>


static inline void _set_desc(unsigned long *desc_addr, 
		unsigned long base, unsigned long limit,
		unsigned long attr)
{
	__asm__(
		"\tmovl %%edx, %0\n"
		"\tmovl %%eax, %1\n"
		::"o"(*(desc_addr)),
		"o"(*((desc_addr)+1)),
		"d"((limit & 0x0000ffff) | ((base << 16) & 0xffff0000)),
		"a"(((base >> 16) & 0x000000ff)|(base & 0xff000000)
		| (limit & 0x000f0000)
		| (attr & DA_MASK))
		);
}
/*
#define _set_desc(desc_addr, base, limit, attr) __asm__(\
		"\tmovl %%edx, %0\n"\
		"\tmovl %%eax, %1\n"\
		::"o"(*((char*)(desc_addr))),\
		"o"(*(4+(char*)(desc_addr))),\
		"d"((limit & 0x0000ffff) | ((base << 16) & 0xffff0000)),\
		"a"(((base >> 16) & 0x000000ff)|(base & 0xff000000) \
		| (limit & 0x000f0000) \
		| (attr & DA_MASK)) \
		)
*/

static inline void _set_gate(unsigned long *gate_addr,
		unsigned long selector, unsigned long offset,
		unsigned long attr)
{
	__asm__(
		"\tmovl %%edx, %0\n"
		"\tmovl %%eax, %1\n"
		::"o"(*(gate_addr)),
		"o"(*(1+(gate_addr))),
		"d"((offset & 0x0000ffff)|((selector << 16) & 0xffff0000)),
		"a"((offset & 0xffff0000)|(attr & GA_MASK)) 
		);
}
/*
#define _set_gate(gate_addr, selector, offset, attr) __asm__(\
		"\tmovl %%edx, %0\n"\
		"\tmovl %%eax, %1\n"\
		::"o"(*((char*)(gate_addr))),\
		"o"(*(4+(char*)(gate_addr))),\
		"d"((offset & 0x0000ffff)|((selector << 16) & 0xffff0000)),\
		"a"((offset & 0xffff0000)|(attr & GA_MASK)) \
		)
*/
		
#define set_trap_gate(idtn_addr, func) \
		_set_gate((unsigned long *)(idtn_addr), SELECTOR_KCS, (unsigned long)(func), (DA_386TGate|DA_DPL0))

#define set_intr_gate(idtn_addr, func) \
		_set_gate((unsigned long *)(idtn_addr), SELECTOR_KCS, (unsigned long)(func), (DA_386IGate|DA_DPL0))

#define set_syscall_gate(idtn_addr, func) \
		_set_gate((unsigned long *)(idtn_addr), SELECTOR_KCS, (unsigned long)(func), (DA_386TGate|DA_DPL3))

#define set_seg_desc(desc_addr, type, base, limit) \
	_set_desc((unsigned long *)(desc_addr), (unsigned long)(base), (unsigned long)(limit), (type))

#define set_ldt_desc(desc_addr, ldt_addr) \
	_set_desc((unsigned long *)(desc_addr), (unsigned long)(ldt_addr),   32, (DA_LIMIT_1B|DA_DPL0|DA_LDT))

#define set_tss_desc(desc_addr, tss_addr) \
	_set_desc((unsigned long *)(desc_addr), (unsigned long)(tss_addr), 104, (DA_LIMIT_1B|DA_DPL0|DA_386TSS))

#define set_desc_base(desc_addr, Base)  ({ \
		((unsigned long *)(desc_addr))[0] = ((((unsigned long *)(desc_addr))[0]) & 0x0000ffff)|(((Base)<<16) & 0xffff0000); \
		((unsigned long *)(desc_addr))[1] = (((Base)>>16) & 0x00ff) |((Base) & 0xff000000)|((((unsigned long *)(desc_addr))[1]) & 0x00ffff00); })

#define set_desc_limit(desc_addr, Limit)  ({ \
		((unsigned long *)(desc_addr))[0] = ((Limit) & 0x0000ffff)|((((unsigned long *)(desc_addr))[0]) & 0xffff0000); \
		((unsigned long *)(desc_addr))[1] = ((Limit) & 0x000f0000)|((((unsigned long *)(desc_addr))[1]) & 0xfff0ffff); })

#define get_base(addr) (((((unsigned long *)(desc_addr))[0]>>16) & 0x0000ffff)|((((unsigned long *)(desc_addr))[1]<<16) & 0x00ff0000)|((((unsigned long *)(desc_addr))[1]) & 0xff000000))

#define get_limit(segment) ({\
		unsigned long limit;\
		__asm__(\
			"\tlsll %%ebx, %%eax\n"\
			"\tincl %%eax\n"\
			:"=a"(limit)\
			:"b"(segment);\
		       )\
		limit;})

#define ltr(selector)	__asm__("ltr  %%ax"::"a"(selector))

#define lldt(selector)	__asm__("lldt %%ax"::"a"(selector))

#define outb(value, port) \
	__asm__("outb %%al, %%dx"::"a"(value),"d"(port))

#define inb(port)  ({\
		unsigned char value;\
		__asm__ volatile("inb %%dx, %%al":"=a"(value):"d"(port));\
		value;\
		})

#define set_cr3(addr)  __asm__("movl %%eax, %%cr3"::"a"(addr))

#define get_cr2() ({\
		unsigned long addr; \
		__asm__ volatile("movl %%cr2, %%eax":"=a"(addr):); \
		addr; \
		})

#define cli()	__asm__ __volatile__("cli"::)

#define sti()   __asm__ __volatile__("sti"::)

#define nop() __asm__ __volatile__("nop"::)

#define hlt()	__asm__ __volatile__("hlt"::)


//从ring0-->ring3:
//USER_CS: 0x0f
//USER_DS: 0x17
//USER_SS: 0x1f
//USER_ESP: 直接继承原esp, 因为原esp指向的就是userStack.
/*
 * 移动到用户态运行时, 运行状态主要发生如下变化:
 * 	1.使用USER_EIP, USER_CS.
 * 	2.使用USER_ESP, USER_SS.
 * 	3.ring0--->ring3.
 *
 * 执行iret时的堆栈结构:
 * USER_SS
 * USER_ESP
 * EFLAGS
 * USER_CS
 * USER_EIP
 * ......
 */
#define move_to_user_mode() __asm__ __volatile__(\
		"\tmovl  %%esp, %%eax\n"\
		"\tpushl $0x1f\n"\
		"\tpushl %%eax\n"\
		"\tpushfl\n"\
		"\tpushl $0x0f\n"\
		"\tpushl $1f\n"\
		"\tiret\n"\
		"1:\t\n"\
		"\tmovw $0x17, %%ax\n"\
		"\tmovw %%ax, %%ds\n"\
		"\tmovw %%ax, %%es\n"\
		"\tmovw %%ax, %%fs\n"\
		::)


static inline void copy_user_to_kernel(void *user_addr, void * kernel_addr, unsigned long length)
{
	__asm__("\t push %%ds \n"
		"\t push %%es \n"
		"\t movw %%ax, %%ds \n"
		"\t cld \n"
		"\t rep \n"
		"\t movsb \n"
		"\t pop %%es \n"
		"\t pop %%ds \n"
		::"a"(SELECTOR_UDS(procs.running_proc->ulevel)),
		 "S"(user_addr),"D"(kernel_addr), "c"(length)
	       );

}
static inline void copy_kernel_to_user(void *kernel_addr, void * user_addr, unsigned long length)
{
	__asm__("\t push %%ds \n"
		"\t push %%es \n"
		"\t movw %%ax, %%es \n"
		"\t cld \n"
		"\t rep \n"
		"\t movsb \n"
		"\t pop %%es \n"
		"\t pop %%ds \n"
		::"a"(SELECTOR_UDS(procs.running_proc->ulevel)),
		 "S"(kernel_addr),"D"(user_addr), "c"(length)
	       );
}

#endif


